#include"socket.hpp"

#include"Epoller.hpp"

#include"commen.hpp"

#include<unordered_map>

#include <functional>

#define EVENT_IN EPOLLIN|EPOLLET

#define EVENT_OUT EPOLLIN|EPOLLET

struct Connection;

class tcpserver;

typedef void(*func_t)(Connection*);

struct Connection
{
    Connection(int fd, func_t recv, func_t send, func_t except, tcpserver* ts, const std::string& ip, uint16_t port):
        _fd(fd),
        _recv_cb(recv),
        _send_cb(send),
        _except_cb(except),
        _ts(ts)
    {
    }

    int _fd;
    std::string _inbuffer; // string 二进制流，vector
    std::string _outbuffer;

    func_t _recv_cb;
    func_t _send_cb;
    func_t _except_cb;

    tcpserver* _ts;

    std::string _ip;
    uint16_t _port;
};

class tcpserver
{
    static const int num = 64;
public:
    tcpserver(int port, func_t OnMessage):
        _port(port),
        _quit(true),
        _OnMessage(OnMessage)
    {
    }

    void init()
    {
        _sk.my_socket();
        _sk.my_bind("0.0.0.0", _port);
        _sk.my_listen();
        AddConnection(_sk.get_sockfd(), EVENT_IN, Accepter, nullptr, nullptr, "0.0.0.0", 0);
        SetNoBlock(_sk.get_sockfd());
        // std::cout << _sk.get_sockfd() << std::endl;
    }

    void AddConnection(int fd, uint32_t event, func_t recv, func_t send, func_t except, const std::string ip, int16_t port)
    {
        Connection* newConnection = new Connection(fd, recv, send, except, this, ip, port);
        _connections[fd] = newConnection;
        _ep.Epoller_Update(fd, EPOLL_CTL_ADD, event);
    }

    void DelConnection(int fd)
    {
        delete _connections[fd];
        _connections.erase(fd);
        _ep.Epoller_Update(fd, EPOLL_CTL_DEL, 0);
    }

    void EventDispatcher(int timeout)
    {
        int n = _ep.Epoller_wait(revs, sizeof(revs));
        if(n < 0)
        {
            perror("Epoller_wait");
            exit(1);
        }
        for(int i = 0; i < n; ++i)
        {
            int fd = revs[i].data.fd;
            uint32_t events = revs[i].events;
            if(_connections.count(fd) != 0)
            {
                if (events & EPOLLERR) // 交给读写处理函数处理
                    events |= (EPOLLIN | EPOLLOUT);
                if (events & EPOLLHUP)
                    events |= (EPOLLIN | EPOLLOUT);
                if(events & EPOLLIN)
                {
                    if(_connections[fd]->_recv_cb) _connections[fd]->_recv_cb(_connections[fd]);
                }
                else if(events & EPOLLOUT)
                {
                    if(_connections[fd]->_send_cb) _connections[fd]->_send_cb(_connections[fd]);
                }
            }
        }
    }

    static void Accepter(Connection* ct)
    {
        while(1)
        {
            sockaddr_in get;
            memset((void*)&get, 0, sizeof(get));
            socklen_t glen = sizeof(get);
            // std::cout << 1 << std::endl;
            int ac_fd = accept(ct->_fd, (sockaddr*)&get, &glen);
            // std::cout << 2 << std::endl;
            if(ac_fd > 0)
            {
                std::cout << "get a new accept" << std::endl;
                char ipstr[64] = {0};
                std::string ip = inet_ntop(AF_INET, (const sockaddr*)&get.sin_addr, ipstr, sizeof(ipstr));
                int16_t port = get.sin_port;
                ct->_ts->AddConnection(ac_fd, EVENT_IN, Recver, Sender, Excepter, ip, port);
                SetNoBlock(ac_fd);
            }
            else
            {
                if (errno == EWOULDBLOCK)
                {                    
                    // std::cout << "resource depletion" << std::endl;
                    break;
                } 
                else if (errno == EINTR)
                    continue;
                else
                    break;
            }
        }
    }

    static void Recver(Connection* ct)
    {
        // std::cout << "Recver fd : " << ct->_fd << std::endl;
        while(1)
        {
            char buffer[1024] = {0};
            ssize_t n = read(ct->_fd, buffer, sizeof(buffer) - 1);
            // std::cout << "read ret:" << n << std::endl;
            if(n > 0)
            {
                buffer[n] = 0;
                ct->_inbuffer += buffer;
            }
            else if(n == 0)
            {
                std::cout << "client is close, server close" << std::endl;
                ct->_ts->Excepter(ct);
                return;
            }
            else
            {
                if (errno == EWOULDBLOCK)
                {                    
                    // std::cout << "resource depletion" << std::endl;
                    break;
                } 
                else if (errno == EINTR)
                    continue;
                else
                {
                    ct->_ts->Excepter(ct);
                    return;
                }
            }
        }
        ct->_ts->_OnMessage(ct);
    }

    static void Sender(Connection* ct)
    {
        // std::cout << 1 << std::endl;
        while(1)
        {
            ssize_t n = write(ct->_fd, ct->_outbuffer.c_str(), ct->_outbuffer.size());
            if(n > 0)
            {
                ct->_outbuffer.erase(0, n);
                if(ct->_outbuffer.empty()) break;
            }
            else if(n == 0)
            {
                break;
            }
            else
            {
                if (errno == EWOULDBLOCK)
                {                    
                    // std::cout << "resource depletion" << std::endl;
                    break;
                } 
                else if (errno == EINTR)
                    continue;
                else
                {
                    ct->_ts->Excepter(ct);
                    break;
                }
            }
        }
        if(ct->_outbuffer.empty())
        {
            ct->_ts->EnableEvent(ct->_fd, true, false);
        }
        else
        {
            ct->_ts->EnableEvent(ct->_fd, true, true);
        }
    }

    static void Excepter(Connection* ct)
    {
        int fd = ct->_fd;
        ct->_ts->DelConnection(fd);
        close(fd);
    }

    void EnableEvent(int fd, bool readable, bool writeable)
    {
        uint32_t events = 0;
        events |= ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
        _ep.Epoller_Update(fd, EPOLL_CTL_MOD, events);
    }

    void PRINT_CONNECTIONS() // debug
    {
        for(auto& e : _connections)
        {
            std::cout << e.first << std::endl;
        }
    }

    void loop()
    {
        _quit = false;
        while(!_quit)
        {
            EventDispatcher(1000);
            PRINT_CONNECTIONS();
            sleep(1);
        }
    }

    ~tcpserver()
    {
        for(auto &e : _connections)
        {
            close(e.first);
            delete e.second;
        }
    }
private:
    sock _sk;
    Epoller _ep;
    std::unordered_map<int, Connection*> _connections;
    struct epoll_event revs[num];
    uint16_t _port;
    bool _quit;
    func_t _OnMessage;
};